from clang.cindex import *
from .cfg import CFG
from .decorators.concrete_node_decorators import *
import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from utils import LLVM_LIBRARY_PATH, is_system_function_declaration, generate_header_path

codes_list = []

def buildAST(filename, include_dirs):
    """This functions builds a Clang AST from a C file."""
    if not Config.loaded:
        Config.set_library_path(LLVM_LIBRARY_PATH)
    index = Index.create()
    try:
        tu = index.parse(filename, args=generate_header_path(include_dirs))
        #tu = index.parse(filename, options=clang.cindex.CursorKind.PARSE_DETAILED_PROCESSING_RECORD)
    except Exception:
        return None
    tu_cursor = tu.cursor
    return tu_cursor

def fillCodeList(filename):
    global codes_list
    with open(filename, "r") as cfile:
        codes_list = cfile.readlines()

def print_global_stmts(stmts, max_func_line, CFG_dict):
    from .cfgblock import extractWithLeadingWhitespace
    if not stmts:
        return
    minLine, minColumn, maxLine, maxColumn = 999999999, 999999999, -1, -1
    minIdx, maxIdx = -1, -1
    #print("CFG:")
    #print("Nodes:")
    #print("Block 0:")
    cfg_block = "Nodes:\nBlock 0:\n"
    for stmt in stmts:
        minLine = stmt.extent.start.line
        if minLine >= max_func_line:
            continue
        minColumn = stmt.extent.start.column
        maxLine = stmt.extent.end.line
        maxColumn = stmt.extent.end.column
        minColumn = extractWithLeadingWhitespace(codes_list[minLine-1], minColumn-1)
        minColumn += 1
        code = codes_list[minLine-1:maxLine]
        if len(code) > 1:
            firstLine = code[0][minColumn-1:]
            lastLine = code[-1][:maxColumn]
            code[0]=firstLine
            code[-1]=lastLine
        else:
            one_line = code[0]
            one_line = one_line[minColumn-1:maxColumn]
            code = [one_line]
        cfg_block += "".join(code)
    cfg_block += "Block 1:\n\nEdges:\n(Block 0) -> (Block 1)\n"
    CFG_dict['global cfg'] = cfg_block
    #print(cfg_block)
    #print("Block 1:")
    #print("")
    #print("Edges")
    #print("(Block 0) -> (Block 1)")


def cfgEntry(filename, include_dirs):
    tu_cursor = buildAST(filename, include_dirs)
    if not tu_cursor:
        return
    fillCodeList(filename)
    # Retrieving the children from the translation Unit
    tu_childs = tu_cursor.get_children()
    cfgs = []
    funcInfos = []
    # for each children of the translation unit, we check if it is a func_decl
    # by looking for the compound stmt
    childs_lst = list(tu_childs)
    stmts = []
    max_func_line = 0
    CFG_dict = dict()
    for child in childs_lst:
        if is_system_function_declaration(child):
            continue
        # If the element is a Func Decl Cursor
        if child.kind is CursorKind.FUNCTION_DECL:
            childs = [c for c in child.get_children()]
            # Check if the last element is a compound statement
            if len(childs) > 0:
                max_func_line = child.extent.end.line
                if childs[-1].kind is CursorKind.COMPOUND_STMT:
                    # FIXIT: accessing to a 'static' variable,
                    # find another way to access to the CursorKind
                    c_stmt = childs[-1]
                    # Transform the cursors of the function and the compound stmt to CFG decorators of
                    # function and compound stmt
                    f_decorator = FunctionDecl(child)
                    c_stmt_decorator = CompoundStmt(c_stmt)
                    # instance of a cfg object
                    cfg = CFG()
                    # build the cfg
                    from .cfg import minFuncLine, maxFuncLine, functionSignature
                    minFuncLine = 99999999999
                    maxFuncLine = -1
                    functionSignature = ""
                    cfg_b = cfg.buildCFG(f_decorator, c_stmt_decorator)
                    # Save the cfg
                    from .cfg import minFuncLine, maxFuncLine, functionSignature
                    cfgs.append([child.spelling, cfg_b, (minFuncLine, maxFuncLine, functionSignature)])
                    #funcInfos.append((maxBlockID, minFuncLine, maxFuncLine, functionSignature))
        else:
            stmts.append(child)
    print_global_stmts(stmts, max_func_line, CFG_dict)
    for cfg_item in cfgs:
        s, cfg_b, infos = cfg_item
        minLine, maxLine, funcSig = infos
        if cfg_b:
            cfg_block = cfg_b.printer(minLine, maxLine, funcSig)
            CFG_dict[s] = cfg_block
    #return cfgs, funcInfos
    return CFG_dict
